/**
 * This file is Copyright © 2008 Software Craftsmen Limited. All Rights Reserved.
 */
package com.softwarecraftsmen.collections.tuples;

import com.softwarecraftsmen.collections.IndexExceedsHighestKnownIndexException;
import com.softwarecraftsmen.collections.IndexLessThanZeroException;
import static com.softwarecraftsmen.collections.tuples.AbstractTuple.tuple;
import static com.softwarecraftsmen.collections.tuples.HasHashCodeEqualTo.hasHashCodeEqualTo;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import org.hamcrest.Matcher;
import static org.hamcrest.Matchers.hasToString;
import static org.junit.Assert.assertThat;
import org.junit.Test;

import java.util.Arrays;
import java.util.List;

public class TupleTest
{
	@Test
	public void equality()
	{
		assertThat(emptyTuple(), is(emptyTuple()));
		assertThat(singleton(), is(singleton()));
		assertThat(pair(), is(pair()));
		assertThat(triple(), is(triple()));
		assertThat(quadruple(), is(quadruple()));
		assertThat(quintuple(), is(quintuple()));
		assertThat(sextuple(), is(sextuple()));
		assertThat(septuple(), is(septuple()));
	}

	@Test
	public void hashCodeEquality()
	{
		assertThat(emptyTuple(), hasHashCodeEqualTo(emptyTuple()));
		assertThat(singleton(), hasHashCodeEqualTo(singleton()));
		assertThat(pair(), hasHashCodeEqualTo(pair()));
		assertThat(triple(), hasHashCodeEqualTo(triple()));
		assertThat(quadruple(), hasHashCodeEqualTo(quadruple()));
		assertThat(quintuple(), hasHashCodeEqualTo(quintuple()));
		assertThat(sextuple(), hasHashCodeEqualTo(sextuple()));
		assertThat(septuple(), hasHashCodeEqualTo(septuple()));
	}

	@Test
	public void toStringIsATuple()
	{
		assertThat(emptyTuple(), hasToString(equalTo("()")));
		assertThat(singleton(), hasToString(equalTo("(Hello)")));
		assertThat(pair(), hasToString(equalTo("(Hello, World)")));
		assertThat(triple(), hasToString(equalTo("(Hello, World, 7)")));
		assertThat(quadruple(), hasToString(equalTo("(Hello, World, 7, Again)")));
		assertThat(quintuple(), hasToString(equalTo("(Hello, World, 7, Again, 5)")));
		assertThat(sextuple(), hasToString(equalTo("(Hello, World, 7, Again, 5, 10)")));
		assertThat(septuple(), hasToString(equalTo("(Hello, World, 7, Again, 5, 10, 7)")));
	}

	@Test
	public void sizeMatchesCardinality()
	{
		assertThat(emptyTuple().size(), is(0));
		assertThat(singleton().size(), is(1));
		assertThat(pair().size(), is(2));
		assertThat(triple().size(), is(3));
		assertThat(quadruple().size(), is(4));
		assertThat(quintuple().size(), is(5));
		assertThat(sextuple().size(), is(6));
		assertThat(septuple().size(), is(7));
	}

	@Test
	public void toLegacyArrayListIsAsExpected()
	{
		assertThat(emptyTuple().toLegacyArrayList(), hasItems());
		assertThat(singleton().toLegacyArrayList(), hasItems("Hello"));
		assertThat(pair().toLegacyArrayList(), hasItems("Hello", "World"));
		assertThat(triple().toLegacyArrayList(), hasItems("Hello", "World", 7));
		assertThat(quadruple().toLegacyArrayList(), hasItems("Hello", "World", 7, "Again"));
		assertThat(quintuple().toLegacyArrayList(), hasItems("Hello", "World", 7, "Again", 5));
		assertThat(sextuple().toLegacyArrayList(), hasItems("Hello", "World", 7, "Again", 5, 10));
		assertThat(septuple().toLegacyArrayList(), hasItems("Hello", "World", 7, "Again", 5, 10, 7));
	}

	@Test
	public void toLegacyLinkedListIsAsExpected()
	{
		assertThat(emptyTuple().toLegacyLinkedList(), hasItems());
		assertThat(singleton().toLegacyLinkedList(), hasItems("Hello"));
		assertThat(pair().toLegacyLinkedList(), hasItems("Hello", "World"));
		assertThat(triple().toLegacyLinkedList(), hasItems("Hello", "World", 7));
		assertThat(quadruple().toLegacyLinkedList(), hasItems("Hello", "World", 7, "Again"));
		assertThat(quintuple().toLegacyLinkedList(), hasItems("Hello", "World", 7, "Again", 5));
		assertThat(sextuple().toLegacyLinkedList(), hasItems("Hello", "World", 7, "Again", 5, 10));
		assertThat(septuple().toLegacyLinkedList(), hasItems("Hello", "World", 7, "Again", 5, 10, 7));
	}

	@Test
	public void slice()
	{
		assertThat(emptyTuple().slice(), is(emptyTuple()));
		assertThat(singleton().slice(), is(emptyTuple()));
		assertThat(pair().slice(), is(singleton()));
		assertThat(triple().slice(), is(pair()));
		assertThat(quadruple().slice(), is(triple()));
		assertThat(quintuple().slice(), is(quadruple()));
		assertThat(sextuple().slice(), is(quintuple()));
		assertThat(septuple().slice(), is(sextuple()));
	}

	// Python permits this, and simply counts from the other end of the list!
	@Test(expected = IndexLessThanZeroException.class)
	public void valueAtNegativeIndex()
	{
		singleton().valueAt(-1);
	}

	@Test(expected = IndexExceedsHighestKnownIndexException.class)
	public void valueAtSize()
	{
		singleton().valueAt(1);
	}

	@Test(expected = IndexExceedsHighestKnownIndexException.class)
	public void valueAtGreaterThanSize()
	{
		singleton().valueAt(2);
	}

	@Test(expected = IndexExceedsHighestKnownIndexException.class)
	public void valueAtForEmptyTuple()
	{
		emptyTuple().valueAt(0);
	}

	@Test
	public void valueAt()
	{
		assertThat(singleton().valueAt(0), is((Object)"Hello"));
		assertThat(pair().valueAt(1), is((Object)"World"));
		assertThat(triple().valueAt(2), is((Object)7));
		assertThat(quadruple().valueAt(3), is((Object)"Again"));
		assertThat(quintuple().valueAt(4), is((Object)5));
		assertThat(sextuple().valueAt(5), is((Object)10));
		assertThat(septuple().valueAt(6), is((Object)7));
	}

	private Matcher<List<Object>> hasItems(final Object ... elements)
	{
		return is(Arrays.asList(elements));
	}

	private EmptyTuple emptyTuple() { return tuple(); }
	private Singleton<String> singleton() { return tuple("Hello"); }
	private Pair<String, String> pair() { return tuple("Hello", "World"); }
	private Triple<String, String, Integer> triple() { return tuple("Hello", "World", 7); }
	private Quadruple<String, String, Integer, String> quadruple() { return tuple("Hello", "World", 7, "Again"); }
	private Quintuple<String, String, Integer, String, Integer> quintuple() { return tuple("Hello", "World", 7, "Again", 5); }
	private Sextuple<String, String, Integer, String, Integer, Integer> sextuple() { return tuple("Hello", "World", 7, "Again", 5, 10); }
	private Septuple<String, String, Integer, String, Integer, Integer, Integer> septuple() { return tuple("Hello", "World", 7, "Again", 5, 10, 7); }
}
